package com.huiruan.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.huiruan.entity.MiniUser;
import com.huiruan.entity.SysUser;
import com.huiruan.service.MiniUserService;
import com.huiruan.service.SysUserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JWT工具类
 */
@Component
public class JwtUtil {
    private static String secret;
    private static Long expiration;
    private static String tokenHeader;
    private static String tokenPrefix;

    private static SysUserService sysUserService;
    private static MiniUserService miniUserService;
    @Autowired
    public void setSysUserService(SysUserService sysUserService) {
        JwtUtil.sysUserService = sysUserService;
    }
    @Autowired
    public void setMiniUserService(MiniUserService miniUserService) {
        JwtUtil.miniUserService = miniUserService;
    }

    @Value("${jwt.secret}")
    public void setSecret(String secret) {
        JwtUtil.secret = secret;
    }

    @Value("${jwt.expiration}")
    public void setExpiration(Long expiration) {
        JwtUtil.expiration = expiration;
    }

    @Value("${jwt.token-header}")
    public void setTokenHeader(String tokenHeader) {
        JwtUtil.tokenHeader = tokenHeader;
    }

    @Value("${jwt.token-prefix}")
    public void setTokenPrefix(String tokenPrefix) {
        JwtUtil.tokenPrefix = tokenPrefix;
    }

    /**
     * 生成token
     */
    public static String generateToken(Long userId, String userType) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userType", userType);
        claims.put("userId", userId);
        return generateToken(claims, "subject");
    }

    /**
     * 从token中获取登录用户
     */
    public static String getSubjectFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.getSubject() : null;
    }

    /**
     * 从token中获取用户类型
     */
    public static String getUserTypeFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? (String) claims.get("userType") : null;
    }
    public static Long getUserIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? ((Number) claims.get("userId")).longValue(): null;
    }
    public static String getIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? (String) claims.get("Id") : null;
    }

    public static String getOpenIdFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? (String) claims.get("openId") : null;
    }

    /**
     * 验证token是否还有效
     */
    public static boolean validateToken(String token, String subject) {
        String username = getSubjectFromToken(token);
        return username.equals(subject) && !isTokenExpired(token);
    }

    /**
     * 判断token是否已经失效
     */
    public static boolean isTokenExpired(String token) {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }

    /**
     * 从token中获取过期时间
     */
    private static Date getExpiredDateFromToken(String token) {
        Claims claims = getClaimsFromToken(token);
        return claims != null ? claims.getExpiration() : null;
    }

    /**
     * 从token中获取JWT中的负载
     */
    private static Claims getClaimsFromToken(String token) {
        if (StrUtil.isEmpty(token)) {
            return null;
        }
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 生成token
     */
    private static String generateToken(Map<String, Object> claims, String subject) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(DateUtil.offsetSecond(new Date(), expiration.intValue()))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
        //return doGenerateToken(claims, "","", 154646L);
    }
    /**
     * 生成Token
     */
    private static String doGenerateToken(Map<String, Object> claims, String subject, String secret, long expiration) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 获取token前缀
     */
    public static String getTokenPrefix() {
        return tokenPrefix;
    }

    /**
     * 获取token头
     */
    public static String getTokenHeader() {
        return tokenHeader;
    }

    /**
     * 从当前请求上下文中获取token并解析用户信息
     * @return 包含用户信息的Map（userId/userType/username），无效token返回null
     */
    public static SysUser getPcUserInfo() {
        // 1. 获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return null;
        }

        HttpServletRequest request = attributes.getRequest();

        // 2. 从请求头获取Authorization
        String authHeader = request.getHeader(getTokenHeader());

        // 3. 检查头信息是否有效
        if (authHeader == null || !authHeader.startsWith(getTokenPrefix())) {
            return null;
        }

        // 4. 提取纯token
        String token = authHeader.replace(getTokenPrefix(), "").trim();

        // 5. 解析用户信息
        return parsePcUserInfo(token);
    }
    public static MiniUser getMiniUserInfo() {
        // 1. 获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return null;
        }

        HttpServletRequest request = attributes.getRequest();

        // 2. 从请求头获取Authorization
        String authHeader = request.getHeader(getTokenHeader());

        // 3. 检查头信息是否有效
        if (authHeader == null || !authHeader.startsWith(getTokenPrefix())) {
            return null;
        }

        // 4. 提取纯token
        String token = authHeader.replace(getTokenPrefix(), "").trim();

        // 5. 解析用户信息
        return parseMiniUserInfo(token);
    }
    /**
     * 从当前请求上下文中获取token并解析用户信息
     * @return 包含用户信息的Map（userId/userType/username），无效token返回null
     */
    public static String getUserId() {
        // 1. 获取当前请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            return null;
        }

        HttpServletRequest request = attributes.getRequest();

        // 2. 从请求头获取Authorization
        String authHeader = request.getHeader(getTokenHeader());

        // 3. 检查头信息是否有效
        if (authHeader == null || !authHeader.startsWith(getTokenPrefix())) {
            return null;
        }

        // 4. 提取纯token
        String token = authHeader.replace(getTokenPrefix(), "").trim();

        // 5. 解析用户信息
        return parseUserId(token);
    }

    /**
     * 解析Token中的用户信息（私有方法）
     */
    private static String parseUserId(String token) {
        try {
            // 验证token有效性
            if (isTokenExpired(token)) {
                return null;
            }

            Claims claims = getClaimsFromToken(token);
            if (claims == null) {
                return null;
            }
            return claims.get("userId", String.class);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 解析Token中的用户信息（私有方法）
     */
    private static SysUser parsePcUserInfo(String token) {
        try {
            // 验证token有效性
            if (isTokenExpired(token)) {
                return null;
            }

            Claims claims = getClaimsFromToken(token);
            if (claims == null) {
                return null;
            }
            String userType = claims.get("userType", String.class);
            Long userId = claims.get("userId", Long.class);
            // 只有当userType是pc且sysUserService不为null时才查询用户详情
            if ("pc".equals(userType) && sysUserService != null) {
                SysUser sysUser = sysUserService.getById(userId);
                if (sysUser != null) {
                    return sysUser;
                }else {
                    return null;
                }
            }
            else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }
    private static MiniUser parseMiniUserInfo(String token) {
        try {
            // 验证token有效性
            if (isTokenExpired(token)) {
                return null;
            }

            Claims claims = getClaimsFromToken(token);
            if (claims == null) {
                return null;
            }
            String userType = claims.get("userType", String.class);
            String id = claims.get("id", String.class);
            // 只有当userType是pc且sysUserService不为null时才查询用户详情
            if ("mini".equals(userType) && miniUserService != null) {
                MiniUser miniUser = miniUserService.getById(id);
                if (miniUser != null) {
                    return miniUser;
                }else {
                    return null;
                }
            }
            else {
                return null;
            }
        } catch (Exception e) {
            return null;
        }
    }
}
